package cn.tedu.micro.demo.emqx.mqtt;

import cn.tedu.micro.demo.emqx.serialize.MessageSerializer;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 回调函数类,需要重建成容器bean对象并且交给绑定一个mqttClient使用
 * 专门负责监听这个mqttClient的一举一动
 * 1. 连接成功
 * 2. 发送消息
 * 3. 断开连接
 * 4. 收到消息
 */
@Component
@Slf4j
public class MqttClientCallback implements MqttCallbackExtended {
    @Autowired
    private MessageSerializer messageSerializer;
    /*
        方法作用: 当mqttClient调用connect并且成功和emqx建立通信之后立即调用
        参数:
        boolean reconnect: 如果本次建立的连接是重新连接,reconnect就是true 第一次连接false
        String serverURI: 连接服务端address
     */
    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        if (reconnect){
            log.info("重新连接成功,broker地址:{}",serverURI);
        }else{
            log.info("首次连接成功,broker地址:{}",serverURI);
        }
    }
    /*
        方法作用: 当mqttClient调用disconnect方法断开连接后,或者由于网络波动,导致和服务端连接通信超时失败,回调就会调用
        这个方法
        参数:
        Throwable cause: 断开连接的原因
     */
    @Override
    public void connectionLost(Throwable cause) {
        log.error("连接断开,原因:{}",cause);
    }

    /*
        这个方法是消费的入口
        方法作用: 当mqttClient连接对象订阅了某个主题,一旦这个主题中出现消息,就会通过这个方法将消息传递给客户端
        参数:
        String topic: 消息来源的topic名称
        MqttMessage message: 消息对象,包含了消息的所有内容
     */
    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        log.info("消息接收成功,消息:{},接收的主题:{}",message,topic);
        //有必要的时候 将json反序列化成object
    }
    /*
       方法作用: 当mqttClient发送消息的时候,只要消息成功发送到emqx,不管emqx是否能够接收到
       反馈接收成功还是失败,都会调用这个deliveryComplete,处理发送完成的业务
       qos服务质量级别中0 表示根本不关心投递发送结果,所以,如果客户端mqttClient使用qos=0级别发送消息,不会有任何异常
       不会回调这个方法
       参数:
       IMqttDeliveryToken token: 发送的消息对象 映射,包含了你发送消息的所有内容,以及双发通信的结果
     */
    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        //1.可以拿到token中的确认结果 结果是true表示发送结束,但不代表发送成功
        boolean ack=token.isComplete();//ack true表示发给服务端了 ack false表示没有发给服务端
        //如果发送是失败 token里会生成一个异常对象
        MqttException exception = token.getException();
        //可以用上述2个数据 才能判断消息是否发送成功
        if (ack&&exception==null){
            log.info("消息发送成功");
        }else{
            log.error("消息发送失败,原因:{}",exception);
        }
    }
}
